﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SRS.Encryption
{
    class BlockCipher
    {
        /// <summary>
        /// prosedur untuk melakukan jaringan feistel
        /// fungsi yang digunakan adalah subtitusi SBox
        /// </summary>
        /// <param name="_BlockInput"></param>
        /// <param name="_SBox"></param>
        /// <returns></returns>
        public static Block Feistel(Block _BlockInput, SBox _SBox)
        {
            Block tempBlock = new Block(_BlockInput.getLengthBlock());
            int halfLength = _BlockInput.getLengthBlock() / 2;
            int fullLength = _BlockInput.getLengthBlock();
            byte temp;

            if (_BlockInput.getByteArray().Length % 2 == 0) // genap
            {
                // mengcopy bagian kanan jadi bagian kiri
                for (int i = 0; i < halfLength; ++i)
                {
                    tempBlock.setElement(i, _BlockInput.getElement(i + halfLength));
                }

                // melakukan subtitus bagian kanan, dan meng-XOR dengan bagian kiri
                for (int i = halfLength; i < fullLength; ++i)
                {
                    temp = _SBox.getByte((int)_BlockInput.getElement(i));   // ambil dari SBox    
                    tempBlock.setElement(i, (byte)(temp ^ _BlockInput.getElement(i - halfLength)));  // XOR dengan bagian kiri
                }
            }
            else     // ganjil
            {
                // mengcopy bagian kanan jadi bagian kiri
                for (int i = 0; i < halfLength; ++i)
                {
                    tempBlock.setElement(i, _BlockInput.getElement(i + halfLength + 1));
                }

                // melakukan subtitus bagian kanan, dan meng-XOR dengan bagian kiri
                for (int i = halfLength + 1; i < fullLength; ++i)
                {
                    temp = _SBox.getByte((int)_BlockInput.getElement(i));   // ambil dari SBox
                    tempBlock.setElement(i, (byte)(temp ^ _BlockInput.getElement(i - halfLength - 1)));  // XOR dengan bagian kiri
                }
                tempBlock.setElement(halfLength, _BlockInput.getElement(halfLength));
            }

            return tempBlock;
        }

        /// <summary>
        /// fungsi yang mengembalikan Block setelah dimasukan ke jaringan feistel
        /// </summary>
        /// <param name="_BlockInput"></param>
        /// <param name="_SBox"></param>
        /// <returns></returns>
        public static Block deFeistel(Block _BlockInput, SBox _SBox)
        {
            Block tempBlock = new Block(_BlockInput.getLengthBlock());
            Block tempBlock2 = new Block(_BlockInput.getLengthBlock());


            int halfLength = _BlockInput.getLengthBlock() / 2;
            int fullLength = _BlockInput.getLengthBlock();
            byte temp;
            //Console.WriteLine("halflength " + halfLength);

            for (int i = 0; i < halfLength; ++i)
            {
                temp = _SBox.getByte((int)_BlockInput.getElement(i));   // ambil dari SBox
                tempBlock2.setElement(i, temp); // memasukan hasil fungsi dengan SBox ke tempBlock2
            }

            if (_BlockInput.getByteArray().Length % 2 == 0) // genap
            {
                // mengcopy bagian kiri jadi bagian kanan
                for (int i = halfLength; i < fullLength; ++i)
                {
                    tempBlock.setElement(i, _BlockInput.getElement(i - halfLength));
                }

                // melakukan operasi untuk mendapatkan bagian kiri
                for (int i = 0; i < halfLength; ++i)
                {
                    temp = (byte)(tempBlock2.getElement(i) ^ _BlockInput.getElement(i + halfLength)); // melakukan XOR pada tempBlock2 dan bagian kanan
                    tempBlock.setElement(i, temp);
                }
            }
            else     // ganjil
            {
                // mengcopy bagian kiri jadi bagian kanan
                for (int i = halfLength + 1; i < fullLength; ++i)
                {
                    tempBlock.setElement(i, _BlockInput.getElement(i - halfLength - 1));
                }

                // melakukan operasi untuk mendapatkan bagian kiri
                for (int i = 0; i < halfLength; ++i)
                {
                    temp = (byte)(tempBlock2.getElement(i) ^ _BlockInput.getElement(i + halfLength + 1)); // melakukan XOR pada tempBlock2 dan bagian kanan
                    tempBlock.setElement(i, temp);
                }
                tempBlock.setElement(halfLength, _BlockInput.getElement(halfLength));
            }

            return tempBlock;
        }

        /// <summary>
        /// fungsi untuk melakukan enkripsi dengan metode SRS
        /// perulangan dilakukan sebanyak panjang kunci
        /// </summary>
        /// <param name="_BlockInput"></param>
        /// <param name="_KeyBlock"></param>
        /// <returns></returns>
        public static Block EncryptSRS(Block _BlockInput, Block _KeyBlock)
        {
            SBox sb = new SBox();

            for (int i = 0; i < _KeyBlock.getLengthBlock() - 1; ++i)
            {
                _BlockInput.eRotate();
                sb.generateSBox((int)_KeyBlock.getElement(i), (int)_KeyBlock.getElement(i + 1));
                _BlockInput = Feistel(_BlockInput, sb);
            }

            _BlockInput.eRotate();
            sb.generateSBox((int)_KeyBlock.getElement(_KeyBlock.getLengthBlock() - 1), (int)_KeyBlock.getElement(0));
            _BlockInput = Feistel(_BlockInput, sb);

            return _BlockInput;
        }

        /// <summary>
        /// fungsi untuk melakukan dekripsi dengan metode SRS
        /// perulangan dilakukan sebanyak panjang kunci
        /// </summary>
        /// <param name="_BlockInput"></param>
        /// <param name="_KeyBlock"></param>
        /// <returns></returns>
        public static Block DecryptSRS(Block _BlockInput, Block _KeyBlock)
        {
            SBox sb = new SBox();

            sb.generateSBox((int)_KeyBlock.getElement(_KeyBlock.getLengthBlock() - 1), (int)_KeyBlock.getElement(0));
            _BlockInput = deFeistel(_BlockInput, sb);
            _BlockInput.dRotate();

            for (int i = _KeyBlock.getLengthBlock() - 1; i > 0; --i)
            {
                sb.generateSBox((int)_KeyBlock.getElement(i - 1), (int)_KeyBlock.getElement(i));
                _BlockInput = deFeistel(_BlockInput, sb);
                _BlockInput.dRotate();
            }

            return _BlockInput;
        }
    }
}
